1   package com.trendmicro.grid.acl.ds.jpa;
2   
3   import com.trendmicro.grid.acl.ds.jpa.entities.JpaPackageFamily;
4   import com.trendmicro.grid.acl.ds.jpa.entities.JpaVendor;
5   import com.trendmicro.grid.acl.l0.datatypes.PackageDetails;
6   import com.trendmicro.grid.acl.l0.datatypes.PackageFamily;
7   import com.trendmicro.grid.acl.l0.datatypes.ProcessPackageDataSet;
8   import com.trendmicro.grid.acl.l0.datatypes.Vendor;
9   import net.sf.tinyjee.config.PropertySection;
10  import org.slf4j.Logger;
11  import org.slf4j.LoggerFactory;
12  
13  import java.util.ArrayList;
14  import java.util.Collection;
15  import java.util.HashMap;
16  import java.util.Map;
17  
18  import static com.trendmicro.grid.acl.ds.config.DataSourcePropertySection.*;
19  import static com.trendmicro.grid.acl.ds.jpa.util.JpaUtils.extractName;
20  import static com.trendmicro.grid.acl.ds.jpa.util.JpaUtils.metadataDiffers;
21  
22  /**
23   * Implements a handler for syncing package families contained in processing results.
24   * <p/>
25   * The handler returns a corresponding collection of package family references that can be
26   * used as references when storing the packages contained in the processing result.
27   *
28   * @author juergen_kellerer, 2010-11-22
29   * @version 1.0
30   */
31  public class ReceivedPackageFamiliesHandler extends AbstractReceivedResultsHandler<Collection<JpaPackageFamily>> {
32  
33  	private static final Logger log = LoggerFactory.getLogger(ReceivedPackageFamiliesHandler.class);
34  
35  	private final JpaVendorRepository vendorRepository;
36  	private final JpaPackageFamilyRepository packageFamilyRepository;
37  
38  	private final boolean createFamilyIfMissing, createVendorIfMissing;
39  	private final boolean presetUpdateFamily, presetUpdateVendor;
40  
41  	/**
42  	 * Creates a new instance that operates on the given repositories and settings.
43  	 *
44  	 * @param vendorRepository		The vendor repository to use for syncing the vendors.
45  	 * @param packageFamilyRepository the PF repository to use for syncing the PackageFamilies.
46  	 */
47  	public ReceivedPackageFamiliesHandler(JpaVendorRepository vendorRepository,
48  										  JpaPackageFamilyRepository packageFamilyRepository) {
49  		PropertySection section = getPropertySection();
50  
51  		this.vendorRepository = vendorRepository;
52  		this.packageFamilyRepository = packageFamilyRepository;
53  
54  		createVendorIfMissing = getValue(section, KEY_CREATE_VENDOR_IF_MISSING);
55  		createFamilyIfMissing = getValue(section, KEY_CREATE_PACKAGE_FAMILY_IF_MISSING);
56  		presetUpdateVendor = getValue(section, KEY_UPDATE_VENDOR_FROM_PROCESS_RESULT);
57  		presetUpdateFamily = getValue(section, KEY_UPDATE_PACKAGE_FAMILY_FROM_PROCESS_RESULT);
58  	}
59  
60  	/**
61  	 * Creates a corresponding collection of package family references that can be used to store packages.
62  	 *
63  	 * @return a corresponding collection of package family references.
64  	 */
65  	@Override
66  	protected Collection<JpaPackageFamily> handle(StorageContext context) {
67  		boolean updateFamily = !context.ignoreAllUpdates && presetUpdateFamily;
68  		boolean updateVendor = !context.ignoreAllUpdates && presetUpdateVendor;
69  
70  		return getPackageFamilyReferences(context.receivedDataSets,
71  			updateFamily, updateVendor, context.ignoreNullMetadataUpdates);
72  	}
73  
74  
75  	Collection<JpaPackageFamily> getPackageFamilyReferences(Collection<ProcessPackageDataSet> dataSets,
76  															boolean updateFamily, boolean updateVendor,
77  															boolean ignoreNullMetadataUpdates) {
78  
79  		final Collection<JpaPackageFamily> refs = new ArrayList<JpaPackageFamily>(dataSets.size());
80  
81  		final Map<String, JpaPackageFamily> cache = updateFamily || updateVendor ? null :
82  			new HashMap<String, JpaPackageFamily>();
83  
84  		for (ProcessPackageDataSet dataSet : dataSets) {
85  			JpaPackageFamily ref;
86  			final PackageDetails details = dataSet.getProcessedPackage();
87  			final String packageName = details.getPackageInformation().getName();
88  			final PackageFamily packageFamily = details.getPackageFamily();
89  			final String familyName = packageFamily == null ? extractName(packageName, 2) : packageFamily.getBasename();
90  
91  			if (cache == null || (ref = cache.get(familyName)) == null) {
92  				JpaVendor vendor = updateVendor ?
93  					getVendorReference(dataSet, updateVendor, ignoreNullMetadataUpdates) :
94  					null;
95  
96  				ref = packageFamily != null && updateFamily ?
97  					packageFamilyRepository.getByName(familyName) :
98  					packageFamilyRepository.getReferenceByName(familyName);
99  
100 				if (ref == null) {
101 					if (!createFamilyIfMissing) {
102 						throw new IllegalArgumentException("The package family '" +
103 							familyName + "' did not exist inside the CoreDB.");
104 					}
105 
106 					if (vendor == null)
107 						vendor = getVendorReference(dataSet, updateVendor, ignoreNullMetadataUpdates);
108 
109 					if (packageFamily == null)
110 						ref = packageFamilyRepository.create(vendor, familyName, familyName, null);
111 					else
112 						ref = packageFamilyRepository.create(vendor, familyName,
113 							packageFamily.getDisplayName(), packageFamily.getMetadata());
114 
115 					log.info("TMACL-00980:Implicitly created new package family: {}", ref);
116 
117 				} else if (updateFamily && packageFamily != null) {
118 					boolean ignoreMetadata = ignoreNullMetadataUpdates && isNull(packageFamily.getMetadata());
119 					boolean changed = !ref.getDisplayName().equals(packageFamily.getDisplayName()) ||
120 						(!ignoreMetadata && metadataDiffers(ref, packageFamily));
121 
122 					if (changed) {
123 						ref.setDisplayName(packageFamily.getDisplayName());
124 						ref.setMetadata(packageFamily.getMetadata());
125 
126 						log.info("TMACL-00990:Updated package family from processing result: {}", ref);
127 					}
128 				}
129 			}
130 
131 			if (cache != null)
132 				cache.put(familyName, ref);
133 			refs.add(ref);
134 		}
135 		return refs;
136 	}
137 
138 
139 	JpaVendor getVendorReference(ProcessPackageDataSet dataSet,
140 								 boolean updateVendor, boolean ignoreNullMetadataUpdates) {
141 
142 		final PackageDetails details = dataSet.getProcessedPackage();
143 		final String packageName = details.getPackageInformation().getName();
144 
145 		Vendor vendor = details.getPackageFamily() != null ? details.getPackageFamily().getVendor() : null;
146 		String name = vendor == null ? extractName(packageName, 1) : vendor.getName();
147 
148 		JpaVendor ref = vendor != null && updateVendor ?
149 			vendorRepository.getByName(name) :
150 			vendorRepository.getReferenceByName(name);
151 
152 		if (ref == null) {
153 			if (!createVendorIfMissing)
154 				throw new IllegalArgumentException("The vendor '" + name + "' did not exist inside the CoreDB.");
155 
156 			if (vendor == null)
157 				ref = vendorRepository.create(name, name, null);
158 			else
159 				ref = vendorRepository.create(name, vendor.getDisplayName(), vendor.getMetadata());
160 
161 			log.info("TMACL-01000:Implicitly created new vendor: {}", ref);
162 
163 		} else if (updateVendor && vendor != null) {
164 			boolean ignoreMetadata = ignoreNullMetadataUpdates && isNull(vendor.getMetadata());
165 			boolean changed = !ref.getDisplayName().equals(vendor.getDisplayName()) ||
166 				(!ignoreMetadata && metadataDiffers(ref, vendor));
167 
168 			if (changed) {
169 				ref.setDisplayName(vendor.getDisplayName());
170 				ref.setMetadata(vendor.getMetadata());
171 
172 				log.info("TMACL-00990:Updated vendor from processing result: {}", ref);
173 			}
174 		}
175 
176 		return ref;
177 	}
178 }