001/* 002 * (C) Copyright 2015 Nuxeo SA (http://nuxeo.com/) and others. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 * 016 * Contributors: 017 * Thierry Delprat <tdelprat@nuxeo.com> 018 */ 019package org.nuxeo.automation.scripting.internals.operation; 020 021import java.net.URL; 022import java.util.Arrays; 023import java.util.Calendar; 024import java.util.List; 025import java.util.Map; 026 027import javax.script.ScriptException; 028 029import org.nuxeo.automation.scripting.internals.ScriptOperationContext; 030import org.nuxeo.ecm.automation.AutomationService; 031import org.nuxeo.ecm.automation.OperationContext; 032import org.nuxeo.ecm.automation.OperationDocumentation; 033import org.nuxeo.ecm.automation.OperationException; 034import org.nuxeo.ecm.automation.core.Constants; 035import org.nuxeo.ecm.automation.core.impl.InvokableMethod; 036import org.nuxeo.ecm.automation.core.impl.OperationTypeImpl; 037import org.nuxeo.ecm.automation.core.scripting.Expression; 038import org.nuxeo.ecm.automation.core.util.BlobList; 039import org.nuxeo.ecm.core.api.Blob; 040import org.nuxeo.ecm.core.api.DocumentModel; 041import org.nuxeo.ecm.core.api.DocumentModelList; 042import org.nuxeo.ecm.core.api.DocumentRef; 043import org.nuxeo.ecm.core.api.DocumentRefList; 044import org.nuxeo.ecm.core.api.NuxeoException; 045 046/** 047 * @since 7.2 048 */ 049public class ScriptingOperationTypeImpl extends OperationTypeImpl { 050 051 protected final AutomationService service; 052 053 protected final ScriptingOperationDescriptor desc; 054 055 protected final InvokableMethod[] methods; 056 057 public ScriptingOperationTypeImpl(AutomationService service, 058 ScriptingOperationDescriptor desc) { 059 this.service = service; 060 this.desc = desc; 061 this.inputType = desc.getInputType(); 062 this.methods = new InvokableMethod[] { runMethod() }; 063 } 064 065 066 @Override 067 public String getContributingComponent() { 068 return null; 069 } 070 071 @Override 072 public OperationDocumentation getDocumentation() { 073 OperationDocumentation doc = new OperationDocumentation(getId()); 074 doc.label = getId(); 075 doc.category = desc.getCategory(); 076 doc.description = desc.getDescription(); 077 doc.params = desc.getParams(); 078 doc.signature = new String[] { desc.getInputType(), desc.getOutputType() }; 079 doc.aliases = desc.getAliases(); 080 return doc; 081 } 082 083 @Override 084 public String getId() { 085 return desc.getId(); 086 } 087 088 @Override 089 public String[] getAliases() { 090 return desc.getAliases(); 091 } 092 093 @Override 094 public List<InvokableMethod> getMethods() { 095 return Arrays.asList(methods); 096 } 097 098 @Override 099 public InvokableMethod[] getMethodsMatchingInput(Class<?> in) { 100 return methods; 101 } 102 103 @Override 104 public AutomationService getService() { 105 return service; 106 } 107 108 @Override 109 public Class<?> getType() { 110 return ScriptingOperationImpl.class; 111 } 112 113 @SuppressWarnings("unchecked") 114 @Override 115 public Object newInstance(OperationContext ctx, Map<String, Object> args) throws OperationException { 116 try { 117 resolveArguments(ctx, args); 118 ScriptOperationContext sctx = new ScriptOperationContext(ctx); 119 return new ScriptingOperationImpl(desc.getScript(), sctx, args); 120 } catch (ScriptException e) { 121 throw new NuxeoException(e); 122 } 123 } 124 125 /** 126 * As 127 * {@link org.nuxeo.ecm.automation.core.impl.OperationTypeImpl#inject(org.nuxeo.ecm.automation.OperationContext, java.util.Map, java.lang.Object)} 128 * is not called in this OperationTypeImpl, we have to inject into arguments all context variables to play the 129 * fallback on chains variables and evaluate script expression like MVEL. 130 * 131 * @since 8.3 132 * @param ctx Automation Context 133 * @param args Operation Parameters 134 */ 135 protected void resolveArguments(OperationContext ctx, Map<String, Object> args) { 136 for (String key : args.keySet()) { 137 if (args.get(key) instanceof Expression) { 138 Object obj = ((Expression) args.get(key)).eval(ctx); 139 args.put(key, obj); 140 } 141 } 142 if (ctx.getVars().containsKey(Constants.VAR_RUNTIME_CHAIN)) { 143 args.putAll((Map<String, Object>) ctx.getVars().get(Constants.VAR_RUNTIME_CHAIN)); 144 } 145 } 146 147 protected String getParamDocumentationType(Class<?> type, boolean isIterable) { 148 String t; 149 if (DocumentModel.class.isAssignableFrom(type) || DocumentRef.class.isAssignableFrom(type)) { 150 t = isIterable ? Constants.T_DOCUMENTS : Constants.T_DOCUMENT; 151 } else if (DocumentModelList.class.isAssignableFrom(type) || DocumentRefList.class.isAssignableFrom(type)) { 152 t = Constants.T_DOCUMENTS; 153 } else if (BlobList.class.isAssignableFrom(type)) { 154 t = Constants.T_BLOBS; 155 } else if (Blob.class.isAssignableFrom(type)) { 156 t = isIterable ? Constants.T_BLOBS : Constants.T_BLOB; 157 } else if (URL.class.isAssignableFrom(type)) { 158 t = Constants.T_RESOURCE; 159 } else if (Calendar.class.isAssignableFrom(type)) { 160 t = Constants.T_DATE; 161 } else { 162 t = type.getSimpleName().toLowerCase(); 163 } 164 return t; 165 } 166 167 protected InvokableMethod runMethod() { 168 try { 169 return new InvokableMethod(this, ScriptingOperationImpl.class.getMethod("run", Object.class)); 170 } catch (NoSuchMethodException | SecurityException e) { 171 throw new UnsupportedOperationException("Cannot use reflection for run method", e); 172 } 173 } 174 175}