1 /* 2 * Copyright (c) 1997, 2015, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package com.sun.xml.internal.ws.spi.db; 27 28 import java.util.ArrayList; 29 import java.util.Iterator; 30 import java.util.List; 31 import java.util.logging.Level; 32 import java.util.logging.Logger; 33 34 import javax.xml.bind.JAXBContext; 35 import javax.xml.bind.Marshaller; 36 37 38 import com.oracle.webservices.internal.api.databinding.DatabindingModeFeature; 39 import com.sun.xml.internal.ws.db.glassfish.JAXBRIContextFactory; 40 import com.sun.xml.internal.ws.util.ServiceConfigurationError; 41 import com.sun.xml.internal.ws.util.ServiceFinder; 42 43 /** 44 * BindingContextFactory 45 * 46 * @author shih-chang.chen@oracle.com 47 */ 48 abstract public class BindingContextFactory { 49 public static final String DefaultDatabindingMode = DatabindingModeFeature.GLASSFISH_JAXB; 50 public static final String JAXB_CONTEXT_FACTORY_PROPERTY = BindingContextFactory.class.getName(); 51 public static final Logger LOGGER = Logger.getLogger(BindingContextFactory.class.getName()); 52 53 // This iterator adds exception checking for proper logging. 54 public static Iterator<BindingContextFactory> serviceIterator() { 55 final ServiceFinder<BindingContextFactory> sf = ServiceFinder 56 .find(BindingContextFactory.class); 57 final Iterator<BindingContextFactory> ibcf = sf.iterator(); 58 59 return new Iterator<BindingContextFactory>() { 60 private BindingContextFactory bcf; 61 62 public boolean hasNext() { 63 while (true) { 64 try { 65 if (ibcf.hasNext()) { 66 bcf = ibcf.next(); 67 return true; 68 } else 69 return false; 70 } catch (ServiceConfigurationError e) { 71 LOGGER.warning("skipping factory: ServiceConfigurationError: " 72 + e.getMessage()); 73 } catch (NoClassDefFoundError ncdfe) { 74 LOGGER.fine("skipping factory: NoClassDefFoundError: " 75 + ncdfe.getMessage()); 76 } 77 } 78 } 79 80 public BindingContextFactory next() { 81 if (LOGGER.isLoggable(Level.FINER)) 82 LOGGER.finer("SPI found provider: " + 83 bcf.getClass().getName()); 84 return bcf; 85 } 86 87 public void remove() { 88 throw new UnsupportedOperationException(); 89 } 90 }; 91 } 92 93 static private List<BindingContextFactory> factories() { 94 List<BindingContextFactory> factories = new java.util.ArrayList<BindingContextFactory>(); 95 Iterator<BindingContextFactory> ibcf = serviceIterator(); 96 while (ibcf.hasNext()) 97 factories.add(ibcf.next()); 98 99 // There should always be at least one factory available. 100 if (factories.isEmpty()) { 101 if (LOGGER.isLoggable(Level.FINER)) 102 LOGGER.log(Level.FINER, "No SPI providers for BindingContextFactory found, adding: " 103 + JAXBRIContextFactory.class.getName()); 104 factories.add(new JAXBRIContextFactory()); 105 } 106 return factories; 107 } 108 109 abstract protected BindingContext newContext(JAXBContext context); 110 111 abstract protected BindingContext newContext(BindingInfo bi); 112 113 /** 114 * Check to see if the BindingContextFactory is for the databinding mode/flavor. The 115 * String parameter can be the package name of the JAXBContext implementation as well. 116 * @param databinding mode/flavor or the package name of the JAXBContext implementation. 117 * @return 118 */ 119 abstract protected boolean isFor(String databinding); 120 121 /** 122 * @deprecated - Does jaxws need this? 123 */ 124 abstract protected BindingContext getContext(Marshaller m); 125 126 static private BindingContextFactory getFactory(String mode) { 127 for (BindingContextFactory f: factories()) { 128 if (f.isFor(mode)) 129 return f; 130 } 131 return null; 132 } 133 134 static public BindingContext create(JAXBContext context) throws DatabindingException { 135 return getJAXBFactory(context).newContext(context); 136 } 137 138 static public BindingContext create(BindingInfo bi) { 139 // Any mode configured in AbstractSEIModelImpl trumps all. 140 // System property comes next, then SPI-located. 141 String mode = bi.getDatabindingMode(); 142 if (mode != null) { 143 if (LOGGER.isLoggable(Level.FINE)) 144 LOGGER.log(Level.FINE, "Using SEI-configured databindng mode: " 145 + mode); 146 } else if ((mode = System.getProperty("BindingContextFactory")) != null) { 147 // The following is left for backward compatibility and should 148 // eventually be removed. 149 bi.setDatabindingMode(mode); 150 if (LOGGER.isLoggable(Level.FINE)) 151 LOGGER.log(Level.FINE, "Using databindng: " + mode 152 + " based on 'BindingContextFactory' System property"); 153 } else if ((mode = System.getProperty(JAXB_CONTEXT_FACTORY_PROPERTY)) != null) { 154 bi.setDatabindingMode(mode); 155 if (LOGGER.isLoggable(Level.FINE)) 156 LOGGER.log(Level.FINE, "Using databindng: " + mode 157 + " based on '" + JAXB_CONTEXT_FACTORY_PROPERTY 158 + "' System property"); 159 } else { 160 // Find a default provider. Note we always ensure the list is always non-empty. 161 BindingContext factory = getBindingContextFromSpi(factories(), bi); 162 if (factory != null) return factory; 163 // Should never get here as the list is non-empty. 164 LOGGER.log(Level.SEVERE, "No Binding Context Factories found."); 165 throw new DatabindingException("No Binding Context Factories found."); 166 } 167 BindingContextFactory f = getFactory(mode); 168 if (f != null) 169 return f.newContext(bi); 170 LOGGER.severe("Unknown Databinding mode: " + mode); 171 throw new DatabindingException("Unknown Databinding mode: " + mode); 172 } 173 174 /** 175 * Creates JAXB bindingContext with one of the provided factories. 176 * To filter appropriate factory {@link BindingContextFactory#isFor(String)} method is used. 177 * Currently known 2 appropriate factories: JAXB RI and MOXY. 178 * In case no suitable factory is found we are trying to create context with any given factory. 179 * 180 * @param factories given collection of factories. 181 * @param bindingInfo will be used to create bindingContext. 182 * @return Created context or null. Null will be returned if we were not able to create context with any given factory. 183 */ 184 private static BindingContext getBindingContextFromSpi(List<BindingContextFactory> factories, BindingInfo bindingInfo) { 185 List<BindingContextFactory> fallback = new ArrayList<BindingContextFactory>(); 186 BindingContext result; 187 for (BindingContextFactory factory : factories) { 188 if (LOGGER.isLoggable(Level.FINE)) { 189 LOGGER.log(Level.FINE, "Found SPI-determined databindng mode: " + factory.getClass().getName()); 190 } 191 if (factory.isFor("org.eclipse.persistence.jaxb") || factory.isFor("com.sun.xml.internal.bind.v2.runtime")) { // filter (JAXB RI || MOXy) implementation 192 result = factory.newContext(bindingInfo); 193 if (result != null) { 194 return result; 195 } 196 } else { 197 if (LOGGER.isLoggable(Level.FINE)) { 198 LOGGER.log(Level.FINE, "Skipped -> not JAXB."); 199 } 200 fallback.add(factory); 201 } 202 } 203 for (BindingContextFactory factory : fallback) { 204 if (LOGGER.isLoggable(Level.FINE)) { 205 LOGGER.log(Level.FINE, "Fallback. Creating from: " + factory.getClass().getName()); 206 } 207 result = getContextOrNullIfError(factory, bindingInfo); 208 if (result != null) { 209 return result; 210 } 211 } 212 return null; 213 } 214 215 /** 216 * Factory creates new context bases on provided bindingInfo. 217 * @param factory given factory. 218 * @param bindingInfo to be used to create context. 219 * @return Created context or null. Null will be returned if an error happened during the creation process. 220 */ 221 private static BindingContext getContextOrNullIfError(BindingContextFactory factory, BindingInfo bindingInfo) { 222 try { 223 return factory.newContext(bindingInfo); 224 } catch (Exception e) { 225 LOGGER.log(Level.WARNING, e.getMessage(), e); 226 return null; 227 } 228 } 229 230 static public boolean isContextSupported(Object o) { 231 if (o == null) return false; 232 String pkgName = o.getClass().getPackage().getName(); 233 for (BindingContextFactory f: factories()) if (f.isFor(pkgName)) return true; 234 return false; 235 } 236 237 static BindingContextFactory getJAXBFactory(Object o) { 238 String pkgName = o.getClass().getPackage().getName(); 239 BindingContextFactory f = getFactory(pkgName); 240 if (f != null) return f; 241 throw new DatabindingException("Unknown JAXBContext implementation: " + o.getClass()); 242 243 } 244 245 /** 246 * @deprecated - Does jaxws need this? 247 */ 248 static public BindingContext getBindingContext(Marshaller m) { 249 return getJAXBFactory(m).getContext(m); 250 } 251 }